<script setup lang="ts">
import type { Recordable } from '@croco/framework/types';

import type { DataTableProps, DynamicsColumnFieldProps, FieldProps, RenderScope, TableColumnActions } from './types';

import type { ContextmenuProps } from '#/components/right-click-menu/index.vue';

import { computed, inject, nextTick, onBeforeMount, onMounted, reactive, ref } from 'vue';

// import { useContentMaximize } from '@croco/framework/hooks';
import { VIcon } from '@croco/framework/icons';
import { ApiStateKey } from '@croco/framework/libraries';

import { ClickOutside as vClickOutside } from 'element-plus';

import { service } from '#/api/request';
import ContextMenu from '#/components/right-click-menu/index.vue';

import DynamicColumn from './dynamic-column.vue';
import DynamicForm from './dynamic-form.vue';
import Toolbar from './toolbar.vue';

const props = withDefaults(defineProps<DataTableProps>(), {
    api: undefined,
    height: '100%',
    index: false,
    border: true,
    stripe: false,
    pageSize: 10,
    lazy: false,
    pagination: true,
    selection: false,
    primaryKey: 'id',
    selectOnRowClick: false,
    formItemWidth: 320,
    beforeOpenContextMenu: () => true,
});

const emits = defineEmits(['rowDbclick', 'loading']);

// const { contentIsMaximize, toggleMaximize } = useContentMaximize();
const fixedParameter = computed(() => props.params);
const params = ref<Recordable<any>>({});
const data = defineModel<any[]>('data');
// const slots = useSlots() as any;
// 通知Page组件错误状态
const onPageState = inject(ApiStateKey);

const tableRef = ref();
const properties = reactive({
    loading: false,
    loadingText: '正在加载...',
    current: undefined,
    selections: [] as any[],
    config: {
        index: props.index,
        border: props.border,
        stripe: props.stripe,
        actionColumn: true,
    },
    data: {
        rows: [],
        total: 0,
    },
    pagination: {
        enabled: props.pagination, // 是否开启分页
        pageSize: props.pageSize,
        pageNum: 1,
        sort: '',
    },
    handle: {
        loadLocalData: (data: any) => {
            properties.data.rows = data?.rows ?? data;
            properties.data.total = data?.total ?? data.length;
        },
        loadRemoteData: (done?: () => void): void => {
            if (props.api) {
                properties.handle.loading(true, 200);
                tableRef.value?.clearSelection(); // 加载前固定清空已选数据
                const ps = { ...properties.pagination, ...fixedParameter.value }; // 基础参数
                params.value && Object.assign(ps, params.value); // 合并参数（被动获取参数）
                console.debug(`发送参数：${JSON.stringify(ps)}`);
                service
                    .get(props.api, ps)
                    .then(({ code, data }) => {
                        properties.data.rows = data.rows ?? [];
                        properties.data.total = data.total ?? 0;
                        properties.handle.loading(false, code);
                        onPageState?.();
                    })
                    .catch(({ code, message }) => {
                        properties.data = { rows: [], total: 0 };
                        properties.handle.loading(false, code ?? 500);
                        // 认证已过期，则跳转登录页面
                        code !== 401 && onPageState?.({ code, message });
                    })
                    .finally(done);
            }
        },
        loading: (status: boolean, code: number) => {
            properties.loading = status;
            emits('loading', status, code);
        },
        sizeChange: (size: number) => {
            properties.pagination.pageSize = size;
            properties.pagination.pageNum = 1;
            properties.handle.loadRemoteData();
        },
        currentPageChange: (num: number) => {
            properties.pagination.pageNum = num;
            if (!properties.loading) {
                properties.handle.loadRemoteData();
            }
        },
        // 复选框-选中行
        selectionChange: (rows: any) => {
            properties.selections = rows || [];
        },
        // 行-单击事件
        rowClick: (row: any, column: any, event: MouseEvent) => {
            event.preventDefault();
            event.stopPropagation();
            onClickRow(row);
        },
        // 行-双击事件
        rowDblClick: (row: { [key: string]: any }) => {
            emits('rowDbclick', row);
            if (props.selectOnRowClick && props.primaryKey) {
                tableRef.value?.clearSelection();
                tableRef.value?.toggleRowSelection(row, undefined);
            }
        },
        // 排序
        sortChange: (column: any) => {
            properties.pagination.sort = column.order ? `${column.prop}:${column.order}` : '';
        },
        // 右键菜单
        rowContextMenu: (row: any, column: any, event: MouseEvent) => {
            // 右键菜单
            event.preventDefault();
            event.stopPropagation();
            contextmenu.onOpen(event, row);
        },
        // 行-样式
        rowClassName(data: { row: never; rowIndex: number }) {
            if (props.selection && properties.selections.some((item) => item[props.primaryKey] === data.row[props.primaryKey])) {
                return 'current-row';
            }
            return '';
        },
    },
});

const onClickRow = (row?: any) => {
    if (row) {
        // 存在复选框时，点击一行则默认选中当前行
        if (props.selection && props.selectOnRowClick && props.primaryKey) {
            tableRef.value?.clearSelection();
            if (row) {
                tableRef.value?.toggleRowSelection(row, true);
            }
        }
    } else {
        tableRef.value?.setCurrentRow();
        tableRef.value?.clearSelection();
    }
};

// 刷新数据
const onRefresh = (data?: any) => {
    if (data === undefined || Object.hasOwn(data, 'code') || Object.hasOwn(data, 'params')) {
        if (data && data.params) {
            Object.assign(params.value, data.params);
        }
        properties.handle.loadRemoteData();
    } else if (Array.isArray(data)) {
        properties.handle.loadLocalData(data);
    }
};

// 刷新后定位
const onRefreshAndLocate = (target: any) => {
    if (props.primaryKey && target) {
        properties.handle.loadRemoteData(() => {
            const item = properties.data.rows.find((o) => o[props.primaryKey] === target);
            if (item) {
                tableRef.value?.setCurrentRow(item);
                tableRef.value?.toggleRowSelection(item, undefined);
            }
        });
    }
};

/** 查询表单 */
const searchFormRef = ref();
const collapsed = ref<boolean>(true); // 查询框是否折叠

/** 自定义列隐藏/显示以及操作列定义 */
const allColumns = computed((): DynamicsColumnFieldProps[] => {
    return (
        props.columns
            ?.filter((o) => !!o.prop)
            ?.map((o, index) => {
                const obj: DynamicsColumnFieldProps = {
                    prop: o.prop ?? '',
                    label: o.label ?? '',
                    visible: o.column?.visible ?? true,
                    no: index,
                    attrs: { ...o.column },
                };
                obj.attrs.showOverflowTooltip = !!o.column?.width;
                return obj;
            }) ?? []
    );
});
const dynamicColumns = ref<DynamicsColumnFieldProps[]>(allColumns.value);
const dynamicColumnVisible = ref<boolean>(false);

// 操作列
const actionColumn = ref<FieldProps | undefined>(props.columns?.find((o) => !o.prop && !o.label));
const showActions = ref<TableColumnActions[]>([]);
const moreActions = ref<TableColumnActions[]>([]);
const moreAuths = ref<string[]>([]);
const moreActionRf = ref();
const moreActionVisible = ref(false);
const actionColumnScope = ref();
if (actionColumn.value && typeof actionColumn.value?.column?.render === 'object') {
    const list = actionColumn.value.column.render as TableColumnActions[];
    const max = ((actionColumn.value.column.maxCollapseButtons ?? 0) > 0 ? actionColumn.value.column.maxCollapseButtons : list.length) as number;
    for (const [i, element] of list.entries()) {
        if (i < max) {
            showActions.value.push(element);
        } else {
            moreActions.value.push(element);
        }
    }
    moreAuths.value = moreActions.value.map((o) => o.auth || '');
}
const onMoreAction = (event: any, scope: RenderScope) => {
    actionColumnScope.value = scope;
    moreActionRf.value = event.target;
    nextTick(() => (moreActionVisible.value = true));
};
const onMoreActionClickOutside = () => {
    // 有多少行数据，该函数就会执行多少次
    if (moreActionVisible.value) {
        moreActionVisible.value = false;
    }
};
// Popover隐藏动画播放完毕后触发，利用动画播放时间差清空选中对象操作。如果onMoreActionClickOutside函数做清空操作会导致执行事件时取不到数据
const onMoreActionPopoverLeave = () => {
    actionColumnScope.value = undefined;
    moreActionRf.value = undefined;
};

/** 右键菜单 */
const contextmenu = reactive<ContextmenuProps>({
    data: {
        position: [0, 0],
        parameter: [],
    },
    menus: [
        {
            id: 'refresh',
            label: '刷新',
            icon: 'icon:refresh',
            hidden: !props.api,
            onClick: () => {
                onRefresh();
            },
        },
        { separator: true },
        {
            label: '设置',
            icon: 'icon:setting',
            children: [
                {
                    id: 'custom_column',
                    label: '自定义列',
                    icon: 'icon:filter',
                    hidden: !props.columns,
                    onClick: () => {
                        dynamicColumnVisible.value = true;
                    },
                },
                {
                    id: 'table_style',
                    label: '表格',
                    children: [
                        {
                            id: 'index',
                            label: '显示序号',
                            onClick: ({ menu }) => {
                                properties.config.index = !properties.config.index;
                                menu.icon = properties.config.index ? 'icon:check' : '';
                            },
                        },
                        {
                            id: 'border',
                            label: '显示边框',
                            onClick: ({ menu }) => {
                                properties.config.border = !properties.config.border;
                                menu.icon = properties.config.border ? 'icon:check' : '';
                            },
                        },
                        {
                            id: 'stripe',
                            label: '显示条纹',
                            onClick: ({ menu }) => {
                                properties.config.stripe = !properties.config.stripe;
                                menu.icon = properties.config.stripe ? 'icon:check' : '';
                            },
                        },
                        {
                            id: 'actionColumn',
                            label: '显示操作列',
                            hidden: showActions.value.length === 0,
                            onClick: ({ menu }) => {
                                properties.config.actionColumn = !properties.config.actionColumn;
                                menu.icon = properties.config.actionColumn ? 'icon:check' : '';
                            },
                        },
                    ],
                },
            ],
        },
    ],
    // 打开菜单前触发事件
    onBeforeOpen: (parameter: any, menus: Array<any>) => {
        menus.forEach((item: any) => {
            switch (item.id) {
                case 'actionColumn': {
                    item.icon = properties.config.actionColumn ? 'icon:check' : '';
                    break;
                }
                case 'border': {
                    item.icon = properties.config.border ? 'icon:check' : '';
                    break;
                }
                case 'index': {
                    item.icon = properties.config.index ? 'icon:check' : '';
                    break;
                }
                case 'stripe': {
                    item.icon = properties.config.stripe ? 'icon:check' : '';
                    break;
                }
            }
        });
        return props.beforeOpenContextMenu(parameter, menus) || true;
    },
    // 打开菜单,使用地方：hadoop
    onOpen: (event: MouseEvent, row?: any) => {
        tableRef.value?.setCurrentRow(row);
        if (row) {
            // 文件资源-右键在非选中行上点击则取消已选中的所有行，同时选中当前行
            const rs = properties.selections?.filter((o: any) => o[props.primaryKey] === row[props.primaryKey]);
            if (rs.length === 0) {
                tableRef.value?.clearSelection();
                tableRef.value?.toggleRowSelection(row, true, false);
                contextmenu.data.parameter = { current: row, selections: [row] };
            } else {
                contextmenu.data.parameter = { current: row, selections: properties.selections };
            }
        } else {
            tableRef.value?.clearSelection();
            contextmenu.data.parameter = { current: undefined, selections: [] };
        }
        nextTick(() => (contextmenu.data.position = [event.clientX, event.clientY]));
    },
});

if (props.contextMenuExtra && Array.isArray(props.contextMenuExtra)) {
    contextmenu.menus.splice(1, 0, ...props.contextMenuExtra);
}

/** 计算表格高度 */
const _height = computed(() => (Number(props.height) ? `${Number(props.height)}px` : props.height));
const searchFormHeight = ref(0);
const tableHeight = computed(() => `calc(100% - 37px * ${props.pagination ? 2 : 1} - 5px - ${searchFormHeight.value}px)`);

const formItems = computed(() => {
    return (
        props.columns
            ?.filter((item) => item.prop && item.search)
            ?.map((item, index) => {
                const itemAttrs = {
                    label: item.label,
                    prop: item.prop,
                    span: 1,
                    order: index * 10,
                };
                if (typeof item.search === 'object') {
                    Object.assign(itemAttrs, item.search);
                }
                return itemAttrs;
            })
            .sort((a, b) => a.order - b.order) ?? []
    );
});
// 初始化参数
onBeforeMount(() => {
    props.columns
        ?.filter((item) => item.prop && item.search)
        ?.forEach((item) => {
            if (typeof item.search === 'object') {
                params.value[item.search.prop ?? item.prop!!] = item.search?.value;
            } else {
                params.value[item.prop!!] = undefined;
            }
        });
});

// 自动加载表数据
onMounted(() => {
    nextTick(() => !props.lazy && !!props.api && properties.handle.loadRemoteData());
});

// 暴露给父组件的参数和方法 (外部需要什么，都可以从这里暴露出去)
defineExpose({
    refresh: onRefresh,
    refreshAndLocate: onRefreshAndLocate,
});
</script>

<template>
    <div class="kt-table" :style="{ height: _height }" style="overflow: hidden" @contextmenu.prevent.stop>
        <div class="kt-table-search" v-if="formItems.length > 0">
            <transition name="fade-down">
                <DynamicForm ref="searchFormRef" v-model="params" :form-items="formItems" v-model:height="searchFormHeight" v-model:collapsed="collapsed" :min-item-width="formItemWidth">
                    <template v-for="formItem in formItems" #[formItem.prop]="slotProps">
                        <slot :name="formItem.prop" v-bind="slotProps"></slot>
                    </template>
                </DynamicForm>
            </transition>
        </div>
        <div class="kt-table-header">
            <div class="kt-table-header__left">
                <slot :selections="properties.selections" name="extra"></slot>
            </div>
            <div class="kt-table-header__right">
                <Toolbar :refresh="!!api" :collapsed="collapsed" :search-ref="searchFormRef" @refresh="onRefresh">
                    <template #default>
                        <slot name="custom-button" :selections="properties.selections"></slot>
                    </template>
                </Toolbar>
            </div>
        </div>
        <div :style="{ height: tableHeight }" @contextmenu.prevent.stop="contextmenu.onOpen" @click.self="onClickRow(null)">
            <el-table
                ref="tableRef"
                :border="properties.config.border"
                :data="data ?? properties.data.rows"
                :tooltip-options="{ popperClass: 'kt-table-tooltip' }"
                :row-key="primaryKey"
                :stripe="properties.config.stripe"
                :highlight-current-row="true"
                v-loading="properties.loading"
                height="100%"
                @row-contextmenu="properties.handle.rowContextMenu"
                @row-click="properties.handle.rowClick"
                @row-dblclick="properties.handle.rowDblClick"
                @selection-change="properties.handle.selectionChange"
                @sort-change="properties.handle.sortChange"
                :row-class-name="properties.handle.rowClassName"
                v-bind="$attrs"
            >
                <el-table-column v-if="selection" align="center" fixed="left" header-align="center" type="selection" width="55" />
                <el-table-column v-if="properties.config.index" align="left" fixed="left" header-align="center" label="序 号" type="index" width="70">
                    <template #default="{ $index }">
                        {{ $index + 1 }}
                    </template>
                </el-table-column>
                <slot></slot>
                <template v-for="(column, index) in dynamicColumns" :key="`column_${index}`">
                    <el-table-column v-if="column.prop && column.visible" :column-key="column.prop" :label="column.label" :prop="column.prop" header-align="center" v-bind="column.attrs">
                        <template #default="scope">
                            <template v-if="column.render && typeof column.render === 'function'">
                                <column-render :scope="scope" :render="column.render" />
                            </template>
                        </template>
                    </el-table-column>
                </template>
                <el-table-column v-if="showActions.length > 0 && properties.config.actionColumn" align="center" fixed="right" label="操作" v-bind="actionColumn?.column">
                    <template #default="scope">
                        <el-button v-for="item in showActions" :key="item.auth" v-auth="item.auth" :type="item.type || 'primary'" @click="item.onClick(scope)" link :title="item.label">
                            <template v-if="item.icon" #icon>
                                <VIcon :icon="item.icon" />
                            </template>
                        </el-button>
                        <el-button v-if="moreActions.length > 0" v-auth:or="moreAuths" v-click-outside="onMoreActionClickOutside" @click="onMoreAction($event, scope)" link title="更多操作">
                            <i class="more-actions"></i>
                        </el-button>
                    </template>
                </el-table-column>
                <template #empty>
                    <el-empty description="暂无数据" :image-size="100" />
                </template>
            </el-table>
        </div>
        <div v-if="properties.pagination.enabled" class="kt-table-footer">
            <el-pagination
                :background="true"
                :current-page="properties.pagination.pageNum"
                :page-size="properties.pagination.pageSize"
                :page-sizes="[10, 20, 50, 100, 200, 500]"
                :pager-count="9"
                :total="properties.data?.total || 0"
                layout="total, sizes, prev, pager, next"
                @size-change="properties.handle.sizeChange"
                @current-change="properties.handle.currentPageChange"
            />
        </div>

        <ContextMenu v-model="contextmenu.menus" :before-open="contextmenu.onBeforeOpen" :data="contextmenu.data" />

        <el-popover
            v-if="showActions.length > 0"
            placement="bottom-end"
            trigger="click"
            :virtual-ref="moreActionRf"
            :virtual-triggering="true"
            :visible="moreActionVisible"
            popper-style="padding: 2px 0"
            @after-leave="onMoreActionPopoverLeave"
        >
            <div class="kt-more-button">
                <el-button v-for="(action, index) in moreActions" v-auth="action.auth" :key="index" text @click="action.onClick(actionColumnScope)">
                    <template v-if="action.icon" #icon>
                        <VIcon :icon="action.icon" />
                    </template>
                    {{ action.label }}
                </el-button>
            </div>
        </el-popover>

        <DynamicColumn v-model="dynamicColumnVisible" v-model:columns="dynamicColumns" direction="rtl" size="20%" />
    </div>
</template>
<style>
.slide-t-enter-active,
.slide-t-leave-active {
    transition: max-height 0.5s ease-in-out;
    overflow: hidden;
}
.slide-t-enter-from,
.slide-t-leave-to {
    max-height: 0;
    opacity: 0;
}
.slide-t-enter-to,
.slide-t-leave-from {
    max-height: 1000px; /* 设置为一个足够大的值 */
    opacity: 1;
}

.more-actions {
    display: inline-block;
    width: 14px;
    height: 14px;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%2350153b' d='M13 7a3 3 0 1 0 6 0a3 3 0 0 0-6 0m0 9a3 3 0 1 0 6 0a3 3 0 0 0-6 0m0 9a3 3 0 1 0 6 0a3 3 0 0 0-6 0'/%3E%3C/svg%3E");
}
</style>
